הרבה אנשים קראו את המאמר על הצפנת תוכן ב-MySQL ושאלו אותי אם יש דרך לעשות הצפנה ופענוח דו-כיווני ב-PHP. מסתבר שיש, ואפילו לא צריך להמציא שום דבר. :-)
פונקציית הצפנה
קודם קוד, אח"כ הסברים:
function encrypt($text, $key, $iv)
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);
}
function decrypt($encrypted_text, $key, $iv)
{
return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted_text, MCRYPT_MODE_CBC, $iv);
}
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC));
$key = 'aoeuaoeuaoeuaoeuaoeaaaaaaaaeeesu'; // max 32 bytes
$enc = encrypt('Hello from phpguide.co.il', $key, $iv);
$dec = decrypt($enc, $key, $iv);
echo 'encrypted: ', $enc , '<br/>decrypted: ',$dec;
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);
}
function decrypt($encrypted_text, $key, $iv)
{
return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted_text, MCRYPT_MODE_CBC, $iv);
}
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC));
$key = 'aoeuaoeuaoeuaoeuaoeaaaaaaaaeeesu'; // max 32 bytes
$enc = encrypt('Hello from phpguide.co.il', $key, $iv);
$dec = decrypt($enc, $key, $iv);
echo 'encrypted: ', $enc , '<br/>decrypted: ',$dec;
אלגוריתם הצפנה
אלגוריתם ההצפנה, הפעולה שנעשית על הטקסט כדי להפוך אותו למוצפן נקראת צופן (cypher). צופן כלשהו יכול להיות החלפה של כל אות באות הבאה אחריה בא'-ב'. בצופן כזה המילה "שלום" תהפוך לערך המוצפן "תמזנ".
mcrypt מכילה כמה אלגוריתמי הצפנה מובנים. הצופן בעל השם rijndael הוא צופן מומלץ, בטיחותי ומהיר לשימוש בימינו.
הפרמטר הראשון של הפונקציות mcrypt_encrypt הוא שם הצופן שבו עליה להשתמש כדי להצפין את הטקסט — MCRYPT_RIJNDAEL_256.
מפתח הצפנה
מפתח הצפנה הוא סוג של סיסמא שבה משתמש האלגוריתם בעת ההצפנה. סיסמה אחרת תיתן תוצאות הצפנה שונה. המפענח חייב לדעת את סיסמת ההצפנה כדי לפענח את הטקסט המוצפן.
לכל אלגוריתם צריך להיות אורך מפתח משלו. rijndael דורש סיסמה באורך 32 byte. סיסמא ארוכה יותר תחזיר שגיאה, סיסמה קצרה יותר תוארך על ידי PHP בעצמה.
זהו הפרמטר השני של הפונקציה mcrypt_encrypt. מפתח (סיסמה) כלשהו שאתם יכולים להמציא לבד. רצוי שיהיה שונה בכל הצפנה שתעשו.
צורת הצפנה
רוב אלגוריתמי ההצפנה יודעים לעבוד רק עם מחרוזת באורך מסוים (בלוק). כדי לאפשר הצפנה דו-כיוונית של נתונים באורך משתנה — הקלט מפורק לבלוקים באורך שהאלגוריתם מצפה. כל בלוק מוצפן בנפרד ולאחר מכן הבלוקים מחוברים לתוצאה יחידה.
אורך הקלט לא תמיד מתחלק לבלוקים מלאים. הבלוק האחרון אינו מלא.
לדוגמא, אם אורך בלוק צפוי הוא 64 byte והקלט הוא 70 byte, בבלוק השני יהיו רק 6 byte. עוד 58 יהיו חסרים ויש להשלים אותם כדי שגם הבלוק החסר יעבור הצפנה.
יש כמה שיטות השלמה לבלוקים חסרים (ecb, cbc, cfb, ofb ועוד).
לא נתמקד באופן הפעולה של כל שיטה, הסבירו את זה טוב יותר בוויקיפדיה.
צורת ההצפנה (MCRYPT_MODE_CBC) היא הפרמטר הרביעי של mcrypt_encrypt.
וקטור אתחול - Initialization Vector
לא להיבהל מהמילה. אלגוריתם הצפנה דורש קצת רנדומליות. וקטור אתחול זה אוסף ערכים רנדומליים שההצפנה תשתמש בהם. כדי שהפענוח יצליח, פונקציית הפענוח צריכה לדעת בדיוק את אותם הערכים הרנדומליים ששימשו להצפנה, ולכן וקטור זה מועבר כפרמטר ולא נוצר לבד על ידי פונקציית ההצפנה.
וקטור אתחול עצמו נוצר על ידי פונקציה ליצירת וקטורי אתחול mcrypt_create_iv. אורך וקטור האתחול חייב להיות באורך שהאלגוריתם דורש. כדי לדעת מה האורך הזה, השתמשנו בפונקציה mcrypt_get_iv_size, שמחזירה את האורך הנדרש עבור האלגוריתם וצורת ההצפנה שבחרנו.
זהו הפרמטר האחרון בפונקציה mcrypt_encrypt.
הפענוח
mcrypt_decrypt עושה שימוש באותם פרמטרים כמו encrypt. היא מקבל את שם האלגוריתם, את מפתח ההצפנה, את הטקסט המוצפן, את צורת ההצפנה ואת וקטור האתחול ומשתמשת בהם כדי לשחזר את הערך המוצפן.
כל הערכים צריכים להיות זהים לאלו ששימשו בהצפנה. לא ניתן להצפין עם אלגוריתם אחד ולנסות לפענח בעזרת אלגוריתם אחר, להצפין עם מפתח אחד ולפענח עם אחר, להצפין עם וקטור אתחול אחד ולפענח עם אחר.
אין שום סיבה להצפין
אחרי שראינו איך הצפנה עובדת — אגלה לכם סוד: רוב הסיכויים שאתם לא צריכים הצפנה.
כנראה שאם אתם קוראים את זה, אתם חושבים על איך לרשום לקוקי של משתמש ערך סודי ביותר.
הבעיה תהיה שלתוך הקוקי תצטרכו לרשום גם את וקטור האתחול, כיוון שבהרצה הבאה שלכם הפונקציה mcrypt_create_iv תיצור וקטור רנדומלי חדש, והוא לא יתאים לפענוח טקסט שהוצפן עם וקטור אחר.
מפחדים שהמשתמש יחליף את הערך בקוקי למשהו אחר? תרשמו לקוקי hash לבדיקה.
$value = '123456';
$salt = 'aoeuscxf0&*)(*)&(*^O)htjhsk';
setcookie('value', $value);
setcookie('validation', md5($value.$salt));
// ----------------------------------------
if( md5($_COOKIE['value'].$salt) != $_COOKIE['validation'] ) echo 'bad hacker!';
$salt = 'aoeuscxf0&*)(*)&(*^O)htjhsk';
setcookie('value', $value);
setcookie('validation', md5($value.$salt));
// ----------------------------------------
if( md5($_COOKIE['value'].$salt) != $_COOKIE['validation'] ) echo 'bad hacker!';
אם המשתמש יחליט לשנות את 123456 למשהו אחר, הוא לא יוכל לשנות את ה-validation לערך נכון, כיוון שאינו יודע את ה-salt שלכם. חמשת השורות האלה ישמרו על בטיחות הערכים שלכם הרבה יותר טוב מכל ההצפנות שתוארו פה.
עוד דוגמא תוכלו למצוא כאן.
סיבה שניה להצפין משהו היא כדי לשמור במסד.
mysql מכילה פונקציות הצפנה מובנות שמתאימות הרבה יותר טוב למטרות אלו.
סיבה שלישית להצפין משהו היא כדי לשלוח אותו למחשב אחר ברשת.
זה המקרה היחידי שבו כנראה תצטרכו את ההצפנה. פה יהיה צורך להחליף את שיטת ההצפנה ל-MCRYPT_MODE_ECB, שלא משתמשת בווקטור אתחול בכלל, אלא רק במפתח הצפנה, עליו שני הצדדים יצטרכו להחליט מראש.
בכל שאר המקרים יש סיכוי שאתם עושים משהו שלא הייתם צריכים לעשות.
רשמו בתגובות בשביל מה אתם משתמשים בהצפנה. :)
תגובות לכתבה:
קודם כל תודה רבה על הרפרנס וההסברים. מאוד עוזר.
יש לי שאלה קטנה - ניסיתי את האלגוריתם וכשאני מפעילה את פונקציית הפענוח אני מקבלת את המחרוזת המקורית בתוספת של סמלים לא מובנים.
זו המחרוזת המקורית: qwER1234
הצפנתי, פיענחתי, ותוצאת הפענוח היא:
qwER1234������������������������
אשמח אם תוכל להכווין אותי למה אני עושה לא נכון שמוסיפ לי את הסמלים לתוצאה.
תודה גדולה,
מור
תוכלי להראות את הקוד שהשתמש בו?
עדיף בתור הודעה בפורום. ובין היתר נסי לבדוק אם הוא מתנהג באופן זהה גם ב
http://phpguide.co.il/phplive
נראה לי שהבנתי מה היתה הבעיה.
המפתח שבו השתמשתי כמו בדוגמה הוא באורך 32.
ייתכן שכדי להצפין האלגוריתם האריך את מחרוזת הסיסמה שלי?
בכל מקרה פשוט השתמשתי ב trim($pwd) וקיבלתי את מחרוזת הסיסמה שרציתי.
תודה רבה!
מאמר מעולה!
אני די בטוח שאורך או תוכן המפתח לא צריכים להשפיע על התוכן המוצפן ולדעתי משהו לא בסדר.
אני רוצה להשתמש בזה להמפנת סיסמאות של משתמשים רשומים באתר שלי , הצפנה עם זה תיהיה יעילה ?
כן. אבל אין שום סיבה שתצפין סיסמה בצורה דו כיוונית.
עדיף להצפין סיסמה בצורה חד כיוונית בהרשמה ולשמור אותה, ואז בהשדהות להצפין את מה שהוא הזין שוב ולהשוות את הערכים המוצפנים. במקרה של שכיחת סיסמה תצטרך לשלוח אליו סיסמה חדשה.